package org.gwu.seuss.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import org.apache.log4j.Logger;
import org.gwu.seuss.communication.exception.AuthenticationException;
import org.gwu.seuss.communication.request.Actions;
import org.gwu.seuss.communication.request.ClientRequest;
import org.gwu.seuss.communication.request.ServerResponse;
import org.gwu.seuss.server.command.Command;
import org.gwu.seuss.server.command.CreateUserCommand;
import org.gwu.seuss.server.command.DeleteFileCommand;
import org.gwu.seuss.server.command.DisableUserCommand;
import org.gwu.seuss.server.command.DownloadCommand;
import org.gwu.seuss.server.command.EnableUserCommand;
import org.gwu.seuss.server.command.ListFilesCommand;
import org.gwu.seuss.server.command.RemoveUserCommand;
import org.gwu.seuss.server.command.ShareCommand;
import org.gwu.seuss.server.command.ShowEventLogsCommand;
import org.gwu.seuss.server.command.ShowStateLogsCommand;
import org.gwu.seuss.server.command.UpdatePasswordCommand;
import org.gwu.seuss.server.command.UpdateUsernameCommand;
import org.gwu.seuss.server.command.UploadCommand;
import org.gwu.seuss.server.exception.NoActionException;
import org.gwu.seuss.service.ServiceFactory;
import org.gwu.seuss.service.UserService;

public class ServerSideSocket extends Thread {
	static Logger log = Logger.getLogger(ServerSideSocket.class);
	private Socket socket;
	private UserService userService = ServiceFactory.getUserService();

	public ServerSideSocket(Socket socket) {
		this.socket = socket;
	}

	public void run() {
		log.info("Performing a request");
		System.out.println("Running");
		ObjectInputStream ois = null;
		ObjectOutputStream oos = null;
		ServerResponse sr = new ServerResponse();
		try {
			ois = new ObjectInputStream(socket.getInputStream());
			oos = new ObjectOutputStream(socket.getOutputStream());

			ClientRequest cr = (ClientRequest) ois.readObject();
			userService.login(cr.getUsername(), cr.getPassword());
			sr = performAction(cr);

		} catch (ClassNotFoundException | IOException e) {
			log.error("Internal error occurred", e);
			sr.setSuccess(false);
			sr.setMessage("Action failed.");
		} catch(AuthenticationException ae) {
			log.error("Failed to authenticate user.", ae);
			sr.setSuccess(false);
			sr.setMessage("Authentication failed.");
		}
		finally {
			try {
				oos.writeObject(sr);
				oos.flush();
				oos.close();
				ois.close();
				socket.close();
			} catch (IOException e) {
				log.error(e.getMessage());
			}
		}
	}

	private ServerResponse performAction(ClientRequest cr) {
		ServerResponse sr = new ServerResponse();

		try {
			Command command = getCommand(cr);
			sr = command.execute();
		} catch (Exception e) {
			log.error("Failed to process request", e);
			System.out.println("Failed to process file");
			sr.setMessage("Action failed");
		}

		return sr;
	}

	private Command getCommand(ClientRequest cr) throws NoActionException {

		// TODO: Change how we handle this. Maybe have a default command?
		if (cr.getAction().equals(Actions.DOWNLOAD)) {
			return new DownloadCommand(cr);
		} else if (cr.getAction().equals(Actions.UPLOAD)) {
			return new UploadCommand(cr);
		} else if (cr.getAction().equals(Actions.CREATE_USER)) {
			return new CreateUserCommand(cr);
		} else if (cr.getAction().equals(Actions.DISABLE_USER)) {
			return new DisableUserCommand(cr);
		} else if (cr.getAction().equals(Actions.ENABLE_USER)) {
			return new EnableUserCommand(cr);
		} else if (cr.getAction().equals(Actions.UPDATE_PASSWORD)) {
			return new UpdatePasswordCommand(cr);
		} else if (cr.getAction().equals(Actions.UPDATE_USERNAME)) {
			return new UpdateUsernameCommand(cr);
		} else if (cr.getAction().equals(Actions.DELETE_FILE)) {
			return new DeleteFileCommand(cr);
		} else if (cr.getAction().equals(Actions.SHOW_EVENT_LOG)) {
			return new ShowEventLogsCommand(cr);
		} else if (cr.getAction().equals(Actions.SHOW_STATE_LOG)) {
			return new ShowStateLogsCommand(cr);
		} else if (cr.getAction().equals(Actions.LIST)) {
			return new ListFilesCommand(cr);
		} else if (cr.getAction().equals(Actions.UPDATE_EMAIL)) {
			return new UpdateUsernameCommand(cr);
		} else if (cr.getAction().equals(Actions.REMOVE_USER)) {
			return new RemoveUserCommand(cr);
		} else if (cr.getAction().equals(Actions.SHARE)) {
			return new ShareCommand(cr);
		} else {
			throw new NoActionException();
		}
	}
}
